package com.supermap.dataservice.grid;

import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.supermap.dataservice.util.Arith;
import ucar.nc2.NetcdfFile;

/**  
 * @Description: 格点数据工具类
 * @author zhoujian
 * @date 2019-4-9
 * @version V1.0 
 */
public class GridTools
{
	
	
	
	public static void main(String[] args) 
	{
		try
		{
			String filePath = "F:\\test\\netcdf\\ec_thin\\TMP\\20190302\\20\\850\\ec_thin\\2019030220_9.nc";
			String element="TMP";
			double lat = 50.22;
			double lon = 130.78;
			float value = getGridValueByLonLat(filePath, element, lon, lat);
			System.out.println(value);
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
		
	}

	/**
	 * 根据经纬度点获取格点数据
	 * @param filePath 文件路径
	 * @param element 文件要素
	 * @param point2dList 目标经纬度点集合
	 * @return Map<Point2D, Float>
	 * @throws Exception
	 */
	public static Map<String, Float> getGridValueByLonLats(String filePath , String element , List<Point2D> point2dList) throws Exception
	{
		Map<String, Float> pointGridMap = null;
		try
		{
			pointGridMap = new HashMap<String, Float>();
			GridData gridData = getGridData(filePath , element);
			pointGridMap = getGridValueByLonLats(gridData , point2dList);
		}
		catch (Exception e)
		{
			throw new Exception(e);
		}
		return pointGridMap;
	}

	/**
	 * 根据经纬度点获取格点数据
	 * @param gridData 格点数据
	 * @param point2dList 目标经纬度集合
	 * @return Map<Point2D, Float>
	 * @throws Exception
	 */
	public static Map<String, Float> getGridValueByLonLats(GridData gridData , List<Point2D> point2dList) throws Exception
	{
		//打开文件
		Map<String, Float> pointGridMap = null;
		float value = 999999f;
		try{
			pointGridMap = new HashMap<String, Float>();
			if (gridData == null)
			{
				throw new Exception("gridData对象数据为空.");
			}
			float startLon = gridData.getLeft();
			float startLat = gridData.getBottom();
			float endLon = gridData.getRight();
			float endLat = gridData.getTop();
			//经度方向分辨率
			float dx = gridData.getDx();
			//纬度方向分辨率
			float dy = gridData.getDy();

			for (Point2D point2d : point2dList)
			{
				float lon = (float) point2d.getX();
				float lat = (float) point2d.getY();

				if (startLat > lat || endLat < lat || startLon > lon || endLon < lon)
				{
					System.out.println(String.format("数据经纬度范围[%f,%f,%f,%f]不包括目标经纬度。", startLon,startLat,endLon,endLat));
					value = 999999f;
				}else{
					//计算目标经纬度在数据下标
					int indexX = 0 , indexY = 0;
					//判断经度方向增长方向[从大到小或者从小到大]
					indexX = dx > 0 ? getIndex(startLon , lon, dx):getIndex(endLon, lon, dx);
					//判断纬度方向增长方向[从大到小或者从小到大]
					indexY = dy > 0 ? getIndex(startLat , lat, dy):getIndex(endLat, lat, dy);
					//获取数据
					float[] valueArray = gridData.getValues();
					value = getValue(valueArray , gridData.getWidth() , indexX , indexY);
				}
				String key = Arith.round(point2d.getX(),6) + ":" + Arith.round(point2d.getY(),6);
				//返回数据
				pointGridMap.put(key, value);
			}
		}catch (Exception ex)
		{
			ex.printStackTrace();
		}
		return  pointGridMap;
	}
	
	/**
	 * 
	 * @Description: 根据经纬度信息获取单点数据
	 * @param filePath nc文件路径
	 * @param element nc文件中要素名
	 * @param lon 目标经度
	 * @param lat 目标纬度
	 * @return float 格点值
	 */
	public static float getGridValueByLonLat(String filePath , String element , double lon , double lat) throws Exception
	{
		return getGridValueByLonLat(filePath,element,(float)lon , (float)lat);
	}
	/**
	 * 
	 * @Description: 根据经纬度信息获取单点数据
	 * @param filePath nc文件路径
	 * @param element nc文件中要素名
	 * @param lon 目标经度
	 * @param lat 目标纬度
	 * @return float 格点值
	 */
	public static float getGridValueByLonLat(String filePath , String element , float lon , float lat) throws Exception
	{
		//打开文件
		float value = 999999f;
		try
		{
			GridData gridData = getGridData(filePath , element);
			if (gridData == null)
			{
				throw new Exception("解析nc["+filePath+","+element+"]文件为空.");
			}
			float startLon = gridData.getLeft();
			float startLat = gridData.getBottom();
			float endLon = gridData.getRight();
			float endLat = gridData.getTop();
			
			if (startLat > lat || endLat < lat || startLon > lon || endLon < lon)
			{
				throw new Exception(String.format("数据经纬度范围[%f,%f,%f,%f]不包括目标经纬度。", startLon,startLat,endLon,endLat));
			}
			//经度方向分辨率
			float dx = gridData.getDx();
			//纬度方向分辨率
			float dy = gridData.getDy();
			
			//计算目标经纬度在数据下标
			int indexX = 0 , indexY = 0;
			//判断经度方向增长方向[从大到小或者从小到大]
			indexX = dx > 0 ? getIndex(startLon , lon, dx):getIndex(endLon, lon, dx);
			//判断纬度方向增长方向[从大到小或者从小到大]
			indexY = dy > 0 ? getIndex(startLat , lat, dy):getIndex(endLat, lat, dy);
			//获取数据
			float[] valueArray = gridData.getValues();
			value = getValue(valueArray , gridData.getWidth() , indexX , indexY);
			//返回数据
		}
		catch (Exception e)
		{
			throw new Exception(e);
		}
		
		
		return value;
	}
	
	public static GridData getGridData(String filePath , String element) throws Exception
	{
		NetcdfFile ncFile = null;
		GridData gridData = null;
		try
		{
			ncFile = NetcdfFile.open(filePath);
			//获取地理范围[x,y,w,h]
			Rectangle2D rectangle2d = NetCDFAllUtil.getGeoBounds(ncFile);
			//获取分辨率
			Point2D point2d = NetCDFAllUtil.getCellSize(ncFile);
		    float dx = (float)Arith.round(point2d.getX(),3);
            float dy = (float) Arith.round(point2d.getY(),3);
			//获取行列号
			int rows = NetCDFAllUtil.getRows(ncFile);
			int clos = NetCDFAllUtil.getCols(ncFile);
			float startLon = (float) Arith.round(rectangle2d.getX(), 3);
			float startLat = (float) Arith.round(rectangle2d.getY(), 3);
			float endLon = (float) Arith.round(rectangle2d.getX() + rectangle2d.getWidth(), 3);
			float endLat = (float) Arith.round(rectangle2d.getY() + rectangle2d.getHeight(), 3);
			//获取数据
			float[] valueArray = NetCDFAllUtil.getValues(element, ncFile);
			 gridData = new GridData(startLon , endLon , endLat , startLat ,dx , dy , clos , rows ,  valueArray);
			
		}
		catch (Exception e)
		{
			throw new Exception("解析netcdf文件异常，异常信息：" + e);
		}finally
		{
			if (ncFile != null){
				ncFile.close();
            }
		}
		return gridData;
	}
	/**
	 * 
	 * @Description: 根据下标从一维数组中获取格点数据
	 * @param valueArray 数据【一维数组】 
	 * @param clos 数据中列数
	 * @param indexX x方向下标
	 * @param indexY y方向下标
	 * @return float
	 */
	private static float getValue(float[] valueArray, int clos,
			int indexX, int indexY)
	{
		return valueArray[clos * indexY + indexX];
	}
	/**
	 * 
	 * @Description: 根据开始经纬度信息和分辨率计算目标经纬度下标
	 * @param start 起始经纬度
	 * @param target 目标经纬度
	 * @param resolution 经纬度分辨率
	 * @return int 下标
	 */
	private static int getIndex(float start , float target , double resolution)
	{
		return (int)Arith.round((Math.abs(target - start)/resolution),0);
	}
	
	public static class GridData{
		 private float left;
		    private float right;
		    private float top;
		    private float bottom;
		    private float dx;
		    private float dy;
		    private int width;
		    private int height;
		    private float[] values;

		    public GridData() {
		    }

		    public GridData(float left, float right, float top, float bottom, float dx, float dy, int width, int height, float[] values) {
		        this.left = left;
		        this.right = right;
		        this.top = top;
		        this.bottom = bottom;
		        this.dx = dx;
		        this.dy = dy;
		        this.width = width;
		        this.height = height;
		        this.values = values;
		    }

		    public float getLeft() {
		        return this.left;
		    }

		    public void setLeft(float left) {
		        this.left = left;
		    }

		    public float getRight() {
		        return this.right;
		    }

		    public void setRight(float right) {
		        this.right = right;
		    }

		    public float getTop() {
		        return this.top;
		    }

		    public void setTop(float top) {
		        this.top = top;
		    }

		    public float getBottom() {
		        return this.bottom;
		    }

		    public void setBottom(float bottom) {
		        this.bottom = bottom;
		    }

		    public float getDx() {
		        return this.dx;
		    }

		    public void setDx(float dx) {
		        this.dx = dx;
		    }

		    public float getDy() {
		        return this.dy;
		    }

		    public void setDy(float dy) {
		        this.dy = dy;
		    }

		    public float[] getValues() {
		        return this.values;
		    }

		    public void setValues(float[] values) {
		        this.values = values;
		    }

		    public int getWidth() {
		        return width;
		    }

		    public void setWidth(int width) {
		        this.width = width;
		    }

		    public int getHeight() {
		        return height;
		    }

		    public void setHeight(int height) {
		        this.height = height;
		    }
	}
}
